<?php

    #-------------------------------------------------------------#
	# This file is part of the Tekuna MVC Microframework for PHP. #
	# (c) 2009 Niels Jäckel <niels.jaeckel@tekuna.org>            #
	# Please visit http://tekuna.org/ for more information.       #
	#                                                             #
	# This software is licensed under the MIT License. Please     #
	# view the LICENSE file for the full text of this license.    #
    #-------------------------------------------------------------#


	Tekuna :: load('org.tekuna.core.i18n.TekunaTranslator');
	Tekuna :: load('org.tekuna.core.i18n.TranslatorException');


	/**
	 * The default TekunaTranslator implementation that uses keys to
	 * identify translation strings. The keys are stored in .ini files
	 * and are read while the first translate() call. Format for translation:
	 *
	 * for static texts:
	 *    %my.key%
	 *
	 * and for texts with additional placeholders:
	 *    %my.placeholder.key|slot.1|slot.2|slot.n%
	 */

	class DefaultTranslator implements TekunaTranslator {

		protected
			$sLanguage = '',
			$sLanguageFile = '';


		/**
		 * Get a new translator instance. Additionaly to the language
		 * the language .ini file can be specified. If this is not set
		 * in the constructor, the setLanguageFile() method must be called
		 * before the first translate() usage.
		 *
		 * @param string $sLanguage the target language
		 * @param string $sLanguageFile optional; the language file for translation
		 */

		public function __construct($sLanguage, $sLanguageFile = NULL) {

			// store the language
			$this -> setLanguage($sLanguage);

			// set language file only if given
			if ($sLanguageFile !== NULL) {

				$this -> setLanguageFile($sLanguageFile);
			}
		}


		/**
		 * Set the target language.
		 *
		 * @param string $sLanguage
		 */

		public function setLanguage($sLanguage) {

			$this -> sLanguage = $sLanguage;
		}


		/**
		 * @return string returns the target language.
		 */

		public function getLanguage() {

			return $this -> sLanguage;
		}


		/**
		 * Set the language .ini file.
		 *
		 * @param string $sLanguageFile
		 */

		public function setLanguageFile($sLanguageFile) {

			$this -> sLanguageFile = $sLanguageFile;
		}


		/**
		 * @return string returns the language .ini file.
		 */

		public function getLanguageFile() {

			return $this -> sLanguageFile;
		}


		protected function getMappings() {

			static $arrMappings = NULL;

			// the complete language file path is stored in the language file attribute
			$sLanguageFilePath = $this -> getLanguageFile();

			// the language file is the same thing relatively to the application root
			$sLanguageFile = str_replace(APPLICATION_ROOT, '', $sLanguageFilePath);

			// load the mappings with the first call
			if (! isset($arrMappings[$sLanguageFile])) {


				// check file existence
				if (! file_exists($sLanguageFilePath) || ! is_readable($sLanguageFilePath)) {

					throw new TranslatorException("The language file '$sLanguageFile' does not exist or is not readable.");
				}

				// load the language mappings
				try {

					$arrMappings[$sLanguageFile] = parse_ini_file($sLanguageFilePath, FALSE);
				}
				catch (PHPWarningException $e) {

					throw new TranslatorException("Error while reading language file '$sLanguageFile'.", $e);
				}
			}

			return $arrMappings[$sLanguageFile];
		}


		/**
		 * Translate the placeholders in the given text to the target language.
		 * The keys are taken from the language .ini file that was set.
		 *
		 * @param string $sText
		 * @return string the input text with translated key placeholders
		 */

		public function translate($sText) {

			// get the key/text mappings
			$arrMappings = $this -> getMappings();

			// replace static placeholders
			$sStaticPlaceholderPattern = '~%([a-zA-Z0-9\.\-\_]{1,100})%~'; // maximum 100 chars between the % chars
			if (preg_match_all($sStaticPlaceholderPattern, $sText, $arrMatches)) {

				// iterate all matches
				foreach ($arrMatches[1] as $sKey) {

					// check existence of the key
					if (isset($arrMappings[$sKey])) {

						$sPlaceholder = "%$sKey%";

						// replace placeholders with stored texts
						$sText = str_replace($sPlaceholder, $arrMappings[$sKey], $sText);
					}
				}
			}

			// replace dynamic placeholders
			$sDynamicPlaceholderPattern = '~%([a-zA-Z0-9\.\-\_]{1,100})(?:\|([^%]+))+%~'; //
			if (preg_match_all($sDynamicPlaceholderPattern, $sText, $arrMatches)) {

				for ($i = 0; $i < count($arrMatches[0]); $i++) {

					// extract the matched values
					$sFullMatch = $arrMatches[0][$i];
					$sKey = $arrMatches[1][$i];
					$arrValues = explode('|', $arrMatches[2][$i]);

					// replace the text
					$sI18NText = $arrMappings[$sKey];

					foreach ($arrValues as $j => $sValue) {

						$j++;
						// replace the {x} placeholders with the given texts
						$sI18NText = str_replace('{'. $j .'}', $sValue, $sI18NText);
					}

					// replace complete match
					$sText = str_replace($sFullMatch, $sI18NText, $sText);
				}
			}


			return $sText;
		}
	}
